﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ValueProviders;
using TasksManager.App.AuthenticationHeaders;
using VehicleRental.API.Models;
using VehicleRental.Data;
using VehicleRental.Models;

namespace VehicleRental.API.Controllers
{
    public class VehiclesController : BaseApiController
    {

        [HttpGet, ActionName("all")]
        public IQueryable<VehicleModel> GetAll()
        {
            VehicleRentalContext context = new VehicleRentalContext();

            var allVehicleEntities = context.Vehicles.Include("Places");

            var allVehicles =
                from vehicleEntity in allVehicleEntities
                select new VehicleModel()
                {
                    Id = vehicleEntity.Id,
                    IsRented = vehicleEntity.IsRented,
                    Manufacturer = vehicleEntity.Manufacturer,
                    Model = vehicleEntity.Model,
                    PricePerHour = vehicleEntity.PricePerHour,
                    RentedDate = vehicleEntity.RentedDate,
                    RentedPeriod = vehicleEntity.RentedPeriod,
                    Type = vehicleEntity.Type,
                    VehicleFee = vehicleEntity.Fee,
                    Year = vehicleEntity.Year,
                    PlaceId =vehicleEntity.Place.Id
                };

            return allVehicles;
        }

        [HttpGet, ActionName("by-place")]
        public IQueryable<VehicleModel> GetByPlace([FromUri]int placeId)
        {
            return this.GetAll().Where(v => v.PlaceId == placeId && v.IsRented == false);
        }

        [HttpGet, ActionName("after-year")]
        public IQueryable<VehicleModel> GetAfterYear([FromUri]int year)
        {
            return this.GetAll().Where(v => v.Year >= year);
        }

        [HttpGet, ActionName("before-year")]
        public IQueryable<VehicleModel> GetBeforeYear([FromUri]int year)
        {
            return this.GetAll().Where(v => v.Year <= year);
        }

        [HttpGet, ActionName("by-type")]
        public IQueryable<VehicleModel> GetBeforeYear([FromUri]VehicleType type)
        {
            return this.GetAll().Where(v => v.Type == type);
        }

        [HttpGet, ActionName("by-manufacturer")]
        public IQueryable<VehicleModel> GetByManufacturer([FromUri]string manufacturer)
        {
            return this.GetAll().Where(v => v.Manufacturer == manufacturer);
        }

        [HttpGet, ActionName("by-model")]
        public IQueryable<VehicleModel> GetByModel([FromUri]string model)
        {
            return this.GetAll().Where(v => v.Model == model);
        }

        [HttpPut, ActionName("rent")]
        public HttpResponseMessage RentVehicle([FromBody]VehicleRentModel rentModel, 
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            return this.ExecuteOperationAndHandleExceptions(() =>
            {
                VehicleRentalContext context = new VehicleRentalContext();

                User currentUser = context.Users.FirstOrDefault(u => u.AccessToken == accessToken);

                if (currentUser == null)
                {
                    throw new ArgumentNullException("You can rent only if you are registerd or logged.");
                }


                Vehicle currentVehicle = context.Vehicles.FirstOrDefault(v => v.Id == rentModel.VehicleId);

                decimal rentalSum = currentVehicle.PricePerHour * rentModel.RentedPeriod;

                Card currentCard = context.Cards.FirstOrDefault(c => c.User.Id == currentUser.Id);

                if (currentCard == null)
                {
                    throw new ArgumentException("Card doesn't exist");
                }

                decimal existingFunds = currentCard.TotalFunds;

                if (existingFunds < rentalSum)
                {
                    throw new ArgumentException("Not enough funds in the card");
                }
                if (rentalSum < 0)
                {
                    throw new ArgumentException("The sum to withdraw should be positive");
                }

                currentCard.TotalFunds -= rentalSum;

                context.SaveChanges();

                currentVehicle.RentedPeriod = rentModel.RentedPeriod;
                currentVehicle.IsRented = true;
                currentVehicle.CurrentOwner = currentUser;
                currentVehicle.RentedDate = DateTime.Now;

                context.SaveChanges();

                return this.Request.CreateResponse(HttpStatusCode.NoContent);
            });
        }


        [HttpPut, ActionName("return")]
        public HttpResponseMessage ReturnVehicle([FromBody]VehicleRentModel rentModel,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
            return this.ExecuteOperationAndHandleExceptions(() =>
            {
                VehicleRentalContext context = new VehicleRentalContext();

                User currentUser = context.Users.FirstOrDefault(u => u.AccessToken == accessToken);

                Vehicle currentVehicle = context.Vehicles.FirstOrDefault(v => v.Id == rentModel.VehicleId);

                decimal rentalSum = currentVehicle.Fee;

                DateTime DueDate = currentVehicle.RentedDate;
                DueDate.AddMinutes(currentVehicle.RentedPeriod);

                if (DateTime.Compare(DueDate, DateTime.Now) < 0)
                {
                    Card currentCard = context.Cards.Include("Users").FirstOrDefault(c => c.User.Id == currentUser.Id);


                    var existingFunds = currentCard.TotalFunds;

                    if (existingFunds < rentalSum)
                    {
                        throw new ArgumentException("Not enough funds in the card");
                    }
                    if (rentalSum < 0)
                    {
                        throw new ArgumentException("The sum to withdraw should be positive");
                    }

                    currentCard.TotalFunds -= rentalSum;
                    context.SaveChanges();
                }

                currentVehicle.RentedPeriod = 0;
                currentVehicle.IsRented = false;
                currentVehicle.CurrentOwner = null;

                context.SaveChanges();

                return this.Request.CreateResponse(HttpStatusCode.NoContent);
            });
        }


        [HttpPost, ActionName("create")]
        public HttpResponseMessage CreateVehicle([FromBody]VehicleCreateModel createModel,
            [ValueProvider(typeof(HeaderValueProviderFactory<string>))] string accessToken)
        {
           return this.ExecuteOperationAndHandleExceptions(() =>
           {
               VehicleRentalContext context = new VehicleRentalContext();

               User currentUser = context.Users.FirstOrDefault(u => u.AccessToken == accessToken);

               if (currentUser == null)
               {
                   throw new ArgumentException("You have to be logged or registerd to create new vehicle");
               }

               //if(!currentUser.UserType == UserType.Seller) => throw new Argument();

               Place currentPlace = context.Places.FirstOrDefault(p => p.Id == createModel.PlaceId);

               if (currentPlace == null)
               {
                   throw new ArgumentNullException("Place doesn't exist");
               }

               Vehicle newVehicle = new Vehicle()
               {
                   Fee = createModel.Fee,
                   Manufacturer = createModel.Manufacturer,
                   Model = createModel.Model,
                   PricePerHour = createModel.PricePerHour,
                   Type = createModel.Type,
                   Year = createModel.Year,
                   Place = currentPlace,
                   RentedDate = DateTime.Now,
                   ImageSource = createModel.ImageSource
               };

               context.Vehicles.Add(newVehicle);
               context.SaveChanges();

               currentPlace.Vehicles.Add(newVehicle);
               context.SaveChanges();

               return this.Request.CreateResponse(HttpStatusCode.Created, VehicleModel.Parse(newVehicle));
           });
        }

        // rent -> user money - hours * pricePerHour
        // return -> check if return date < rentedDate.AddHours(rentedPeriod) if not -> user.money - vehicle.Fee
        // filters (with get-all private function):
        // by-year/by-manufacturer/by-model/by-place
        // add new vehicle()
    }
}
